// BigInt.h
#pragma once

#include <iostream>

using std::istream;
using std::ostream;
namespace Jidantou {
    class BigInt{
    public:
        // can be changed if necessary
        enum { LONGLONG_NUM = 4 };

        // use int64_t to initialize
        BigInt(int64_t num = 0);

        // copy constructor function
        BigInt& operator=(const BigInt& num2);

        // negate the number, using Complement
        BigInt operator-() const;

        // add num2 to this
        BigInt operator+(const BigInt& num2) const;

        // this minus num2
        BigInt operator-(const BigInt& num2) const;

        // multiply this by num2
        BigInt operator*(BigInt num2) const;

        // this divided by num2
        BigInt operator/(BigInt num2) const;

        // this divided by num2 and get the remainder
        BigInt operator%(BigInt num2) const;

        // add num2 to this, saved in this
        BigInt& operator+=(const BigInt& num2);

        // this minus num2, saved in this
        BigInt& operator-=(const BigInt& num2);

        // multiply this by num2, saved in this
        BigInt& operator*=(const BigInt& num2);

        // this divided by num2, saved in this
        BigInt& operator/=(const BigInt& num2);

        // this divided by num2 and save the remainder into this
        BigInt& operator%=(const BigInt& num2);

        // ++(*this)
        BigInt& operator++();

        // (*this)++
        BigInt operator++(int);

        // --(*this)
        BigInt& operator--();

        // (*this)--
        BigInt operator--(int);

        // if *this == num2, return true
        bool operator==(const BigInt& num2) const;

        // if *this != num2, return true
        bool operator!=(const BigInt& num2) const;

        // if *this > num2, return true
        bool operator>(const BigInt& num2) const;

        // if *this < num2, return true
        bool operator<(const BigInt& num2) const;

        // if *this >= num2, return true
        bool operator>=(const BigInt& num2) const;

        // if *this <= num2, return true
        bool operator<=(const BigInt& num2) const;

        // return *this & num2, include symbol bit
        BigInt operator&(const BigInt& num2) const;

        // return *this | num2, include symbol bit
        BigInt operator|(const BigInt& num2) const;

        // return *this ^ num2, include symbol bit
        BigInt operator^(const BigInt& num2) const;

        // return ~(*this), include symbol bit
        BigInt operator~() const;

        // save *this & num2 into this, include symbol bit
        BigInt& operator&=(const BigInt& num2);

        // save *this | num2 into this, include symbol bit
        BigInt& operator|=(const BigInt& num2);

        // save *this ^ num2 into this, include symbol bit
        BigInt& operator^=(const BigInt& num2);

        // return (*this) << offset, don't change symbol bit
        BigInt operator<<(const unsigned int offset) const;

        // return (*this) >> offset, don't change symbol bit
        BigInt operator>>(const unsigned int offset) const;

        // save (*this) << offset into this, don't change symbol bit
        BigInt& operator<<=(const unsigned int offset);

        // save (*this) << offset into this, don't change symbol bit
        BigInt& operator>>=(const unsigned int offset);

        // turn string into BigInt& num
        // size include the border of str, include '\0'
        // return the length of str, include '\0'
        // if error, return 0 or negative number for the wrong index
        friend int strtobigint(const char* str, const int size, BigInt& num);

        // turn num into string
        // size include the border of str, include '\0'
        // return the length of str, include '\0'
        // if error, return 0 or negative number for the wrong index
        friend int biginttostr(char* str, const int size, BigInt num);

        // destructor function
        ~BigInt();

    private:
        // saved the data with complement
        uint64_t _number[LONGLONG_NUM];

        // 0 for positive, 1 for negetive
        int _sign;
    };

    // get the number by istream, can identify negative numbers
    // currently only support decimal system
    istream& operator>>(istream& in, BigInt& num);

    // output num by ostream, can output negative system
    // currently only support output decimal format
    ostream& operator<<(ostream& out, const BigInt& num);
}